#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <mcp_can.h>
#include <SPI.h>

// WiFi 配置
const char* ssid = "ESP32-OBD-Reader";
const char* password = "12345678";

// Web 服务器
WebServer server(80);

// CAN 变量
long unsigned int rxId = 0;
unsigned char len = 0;
unsigned char rxBuf[8] = {0};
char msgString[128] = {0};

// 安全引脚定义 - 避免启动冲突
#define VSPI_MOSI 23   // VSPI MOSI - 安全
#define VSPI_MISO 19   // VSPI MISO - 安全  
#define VSPI_SCK  18   // VSPI SCK - 安全
#define CAN0_CS   5    // CS - 安全 (不要用 GPIO15, GPIO0, GPIO2)
#define CAN0_INT  4    // INT - 安全 (不要用 GPIO2 - 蓝灯引脚)

MCP_CAN CAN0(CAN0_CS);

// OBD 数据结构
struct OBDData {
  float engineRPM = 0;
  float vehicleSpeed = 0;
  float coolantTemp = 0;
  float throttlePosition = 0;
  float engineLoad = 0;
  float intakeAirTemp = 0;
  float fuelLevel = 0;
  unsigned long lastUpdate = 0;
  bool canConnected = false;
  String statusMessage = "系统初始化中...";
  unsigned long canMessageCount = 0;
};

OBDData obdData;

// HTML 页面（保持不变）
const char* htmlPage = R"rawliteral(
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ESP32 OBD数据监控</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
        body { background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d); color: #fff; min-height: 100vh; padding: 20px; }
        .container { max-width: 1200px; margin: 0 auto; }
        header { text-align: center; padding: 20px 0; margin-bottom: 30px; }
        h1 { font-size: 2.5rem; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); }
        .status-bar { display: flex; justify-content: space-between; background: rgba(0, 0, 0, 0.3); padding: 15px; border-radius: 10px; margin-bottom: 20px; backdrop-filter: blur(10px); }
        .status-item { text-align: center; }
        .status-value { font-size: 1.8rem; font-weight: bold; margin-top: 5px; }
        .gauges { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 30px; }
        .gauge-container { background: rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 20px; text-align: center; backdrop-filter: blur(10px); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); }
        .gauge-title { font-size: 1.2rem; margin-bottom: 15px; color: #e0e0e0; }
        .gauge { position: relative; width: 200px; height: 200px; margin: 0 auto; }
        .gauge-value { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 2rem; font-weight: bold; }
        .gauge-unit { font-size: 1rem; margin-left: 5px; }
        .data-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; }
        .data-card { background: rgba(255, 255, 255, 0.1); border-radius: 10px; padding: 15px; display: flex; justify-content: space-between; align-items: center; backdrop-filter: blur(10px); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); }
        .data-label { font-size: 1rem; color: #e0e0e0; }
        .data-value { font-size: 1.5rem; font-weight: bold; }
        .last-update { text-align: center; margin-top: 20px; font-size: 0.9rem; color: #b0b0b0; }
        .status-message { text-align: center; margin: 15px 0; padding: 10px; background: rgba(255, 255, 255, 0.1); border-radius: 5px; font-size: 0.9rem; }
        @media (max-width: 768px) {
            .gauges { grid-template-columns: 1fr; }
            .status-bar { flex-direction: column; gap: 15px; }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>ESP32 OBD数据监控</h1>
            <p>实时车辆数据监测系统</p>
        </header>
        <div class="status-message" id="status-message">系统初始化中...</div>
        <div class="status-bar">
            <div class="status-item"><div>连接状态</div><div class="status-value" id="connection-status">检测中...</div></div>
            <div class="status-item"><div>数据更新时间</div><div class="status-value" id="update-time">--:--:--</div></div>
            <div class="status-item"><div>CAN总线状态</div><div class="status-value" id="can-status">检测中...</div></div>
        </div>
        <div class="gauges">
            <div class="gauge-container"><div class="gauge-title">发动机转速</div><div class="gauge"><svg width="200" height="200" viewBox="0 0 200 200"><circle cx="100" cy="100" r="90" stroke="#333" stroke-width="10" fill="none"/><circle cx="100" cy="100" r="90" stroke="#4CAF50" stroke-width="10" fill="none" stroke-dasharray="565.48" stroke-dashoffset="282.74" id="rpm-gauge"/></svg><div class="gauge-value" id="rpm-value">0<span class="gauge-unit">RPM</span></div></div></div>
            <div class="gauge-container"><div class="gauge-title">车速</div><div class="gauge"><svg width="200" height="200" viewBox="0 0 200 200"><circle cx="100" cy="100" r="90" stroke="#333" stroke-width="10" fill="none"/><circle cx="100" cy="100" r="90" stroke="#2196F3" stroke-width="10" fill="none" stroke-dasharray="565.48" stroke-dashoffset="565.48" id="speed-gauge"/></svg><div class="gauge-value" id="speed-value">0<span class="gauge-unit">km/h</span></div></div></div>
            <div class="gauge-container"><div class="gauge-title">发动机负载</div><div class="gauge"><svg width="200" height="200" viewBox="0 0 200 200"><circle cx="100" cy="100" r="90" stroke="#333" stroke-width="10" fill="none"/><circle cx="100" cy="100" r="90" stroke="#FF9800" stroke-width="10" fill="none" stroke-dasharray="565.48" stroke-dashoffset="565.48" id="load-gauge"/></svg><div class="gauge-value" id="load-value">0<span class="gauge-unit">%</span></div></div></div>
        </div>
        <div class="data-grid">
            <div class="data-card"><div class="data-label">冷却液温度</div><div class="data-value" id="coolant-temp">0 °C</div></div>
            <div class="data-card"><div class="data-label">节气门位置</div><div class="data-value" id="throttle-pos">0 %</div></div>
            <div class="data-card"><div class="data-label">进气温度</div><div class="data-value" id="intake-temp">0 °C</div></div>
            <div class="data-card"><div class="data-label">燃油液位</div><div class="data-value" id="fuel-level">0 %</div></div>
        </div>
        <div class="last-update">最后更新: <span id="last-update-time">--:--:--</span> | CAN消息: <span id="can-message-count">0</span></div>
    </div>

    <script>
        function updateData() {
            fetch('/data').then(response => {
                if (!response.ok) throw new Error('网络响应不正常');
                return response.json();
            }).then(data => {
                document.getElementById('status-message').textContent = data.statusMessage;
                document.getElementById('rpm-value').innerHTML = data.rpm + '<span class="gauge-unit">RPM</span>';
                document.getElementById('speed-value').innerHTML = data.speed + '<span class="gauge-unit">km/h</span>';
                document.getElementById('load-value').innerHTML = data.load + '<span class="gauge-unit">%</span>';
                updateGauge('rpm-gauge', data.rpm, 8000);
                updateGauge('speed-gauge', data.speed, 200);
                updateGauge('load-gauge', data.load, 100);
                document.getElementById('coolant-temp').textContent = data.coolantTemp + ' °C';
                document.getElementById('throttle-pos').textContent = data.throttlePos + ' %';
                document.getElementById('intake-temp').textContent = data.intakeTemp + ' °C';
                document.getElementById('fuel-level').textContent = data.fuelLevel + ' %';
                document.getElementById('can-status').textContent = data.canConnected ? '正常' : '断开';
                document.getElementById('can-status').style.color = data.canConnected ? '#4CAF50' : '#f44336';
                document.getElementById('connection-status').textContent = '已连接';
                document.getElementById('connection-status').style.color = '#4CAF50';
                document.getElementById('can-message-count').textContent = data.canMessageCount;
                document.getElementById('last-update-time').textContent = new Date().toLocaleTimeString();
            }).catch(error => {
                console.error('获取数据失败:', error);
                document.getElementById('connection-status').textContent = '连接失败';
                document.getElementById('connection-status').style.color = '#f44336';
                document.getElementById('status-message').textContent = '数据获取失败: ' + error.message;
            });
        }
        function updateGauge(gaugeId, value, max) {
            const gauge = document.getElementById(gaugeId);
            const percentage = Math.min(value / max, 1);
            const dashoffset = 565.48 - (565.48 * percentage);
            gauge.style.strokeDashoffset = dashoffset;
            gauge.style.stroke = percentage < 0.5 ? '#4CAF50' : percentage < 0.8 ? '#FF9800' : '#f44336';
        }
        setInterval(updateData, 2000);
        window.onload = updateData;
    </script>
</body>
</html>
)rawliteral";

// 函数声明
void initSPI();
void initCAN();
void setupWiFi();
void setupWebServer();
void handleRoot();
void handleData();
void readOBDData();
void parseCANFrame(unsigned long canId, unsigned char* buf, unsigned char len);
String getOBDJSONData();

void setup() {
  Serial.begin(115200);
  Serial.println("启动 ESP32 OBD 数据读取器 (安全引脚)...");
  
  // 打印引脚信息
  Serial.println("引脚配置:");
  Serial.print("MOSI: GPIO"); Serial.println(VSPI_MOSI);
  Serial.print("MISO: GPIO"); Serial.println(VSPI_MISO);
  Serial.print("SCK:  GPIO"); Serial.println(VSPI_SCK);
  Serial.print("CS:   GPIO"); Serial.println(CAN0_CS);
  Serial.print("INT:  GPIO"); Serial.println(CAN0_INT);
  
  // 初始化 SPI
  initSPI();
  
  // 初始化 CAN
  initCAN();
  
  // 设置 WiFi
  setupWiFi();
  
  // 设置 Web 服务器
  setupWebServer();
  
  Serial.println("系统启动完成");
  Serial.print("请连接 WiFi: ");
  Serial.println(ssid);
  Serial.print("密码: ");
  Serial.println(password);
  Serial.print("然后访问: http://");
  Serial.println(WiFi.softAPIP());
}

void loop() {
  server.handleClient();
  readOBDData();
  delay(10);
}

void initSPI() {
  Serial.println("初始化 SPI...");
  SPI.begin(VSPI_SCK, VSPI_MISO, VSPI_MOSI);
  delay(100);
}

void initCAN() {
  Serial.println("初始化 MCP2515...");
  
  // 设置中断引脚
  pinMode(CAN0_INT, INPUT);
  
  // 初始化 MCP2515
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) {
    Serial.println("MCP2515 初始化成功!");
    CAN0.setMode(MCP_NORMAL);
    obdData.canConnected = true;
    obdData.statusMessage = "CAN 总线连接成功";
  } else {
    Serial.println("MCP2515 初始化失败!");
    obdData.canConnected = false;
    obdData.statusMessage = "CAN 总线连接失败";
  }
}

void setupWiFi() {
  Serial.println("设置 WiFi 热点...");
  WiFi.mode(WIFI_AP);
  
  if (WiFi.softAP(ssid, password)) {
    IPAddress IP = WiFi.softAPIP();
    Serial.print("AP IP 地址: ");
    Serial.println(IP);
  } else {
    Serial.println("WiFi 热点启动失败!");
  }
}

void setupWebServer() {
  Serial.println("设置 Web 服务器...");
  server.on("/", HTTP_GET, handleRoot);
  server.on("/data", HTTP_GET, handleData);
  server.onNotFound([]() {
    server.send(404, "text/plain", "页面未找到");
  });
  server.begin();
  Serial.println("HTTP 服务器启动");
}

void handleRoot() {
  server.send(200, "text/html", htmlPage);
}

void handleData() {
  server.send(200, "application/json", getOBDJSONData());
}

void readOBDData() {
  if (!digitalRead(CAN0_INT)) {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);
    
    obdData.canMessageCount++;
    parseCANFrame(rxId, rxBuf, len);
    obdData.lastUpdate = millis();
    obdData.statusMessage = "实时数据接收中...";
    
    // 调试输出
    if ((rxId & 0x80000000) == 0x80000000) {
      sprintf(msgString, "扩展 ID: 0x%.8lX DLC: %1d 数据:", (rxId & 0x1FFFFFFF), len);
    } else {
      sprintf(msgString, "标准 ID: 0x%.3lX DLC: %1d 数据:", rxId, len);
    }
    
    Serial.print(msgString);
    
    if ((rxId & 0x40000000) == 0x40000000) {
      Serial.print(" 远程请求帧");
    } else {
      for (byte i = 0; i < len; i++) {
        sprintf(msgString, " 0x%.2X", rxBuf[i]);
        Serial.print(msgString);
      }
    }
    Serial.println();
  }
  
  // 模拟数据
  static unsigned long lastSimUpdate = 0;
  if (!obdData.canConnected && millis() - lastSimUpdate > 2000) {
    obdData.engineRPM = 1500 + random(500);
    obdData.vehicleSpeed = 60 + random(20);
    obdData.coolantTemp = 85 + random(10);
    obdData.throttlePosition = 20 + random(15);
    obdData.engineLoad = 30 + random(40);
    obdData.intakeAirTemp = 25 + random(10);
    obdData.fuelLevel = 70 + random(20);
    obdData.statusMessage = "模拟数据模式 - CAN 未连接";
    lastSimUpdate = millis();
  }
}

void parseCANFrame(unsigned long canId, unsigned char* buf, unsigned char len) {
  if (canId == 0x7E8) {
    if (buf[1] == 0x0C && len >= 4) {
      obdData.engineRPM = (buf[2] * 256 + buf[3]) / 4;
    } else if (buf[1] == 0x0D && len >= 3) {
      obdData.vehicleSpeed = buf[2];
    } else if (buf[1] == 0x05 && len >= 3) {
      obdData.coolantTemp = buf[2] - 40;
    } else if (buf[1] == 0x11 && len >= 3) {
      obdData.throttlePosition = (100.0 * buf[2]) / 255.0;
    } else if (buf[1] == 0x04 && len >= 3) {
      obdData.engineLoad = (100.0 * buf[2]) / 255.0;
    } else if (buf[1] == 0x0F && len >= 3) {
      obdData.intakeAirTemp = buf[2] - 40;
    } else if (buf[1] == 0x2F && len >= 3) {
      obdData.fuelLevel = (100.0 * buf[2]) / 255.0;
    }
  }
}

String getOBDJSONData() {
  DynamicJsonDocument doc(1024);
  doc["rpm"] = obdData.engineRPM;
  doc["speed"] = obdData.vehicleSpeed;
  doc["coolantTemp"] = obdData.coolantTemp;
  doc["throttlePos"] = obdData.throttlePosition;
  doc["load"] = obdData.engineLoad;
  doc["intakeTemp"] = obdData.intakeAirTemp;
  doc["fuelLevel"] = obdData.fuelLevel;
  doc["canConnected"] = obdData.canConnected;
  doc["lastUpdate"] = obdData.lastUpdate;
  doc["statusMessage"] = obdData.statusMessage;
  doc["canMessageCount"] = obdData.canMessageCount;
  
  String jsonString;
  serializeJson(doc, jsonString);
  return jsonString;
}